TypeScript 提供了多種內建的 Utility 型別,它是一組 內建的型別操作工具
,可以幫助我們更輕鬆、更有效率地進行型別操作,今天威爾豬先介紹基本且常用的 Utility 型別:
前面威爾豬有稍微提到 可選串連 (?)
,而 Partial<T>
就是用於將物件型別的 所有屬性變為可選屬性
,這對於在撰寫物件型別或函式參數時,允許部分屬性更新非常有用,可以減少重複的程式碼,更方便地操作物件。
用法:Partial<型別名稱>
例如:
interface IPerson {
name: string;
age: number;
address: string;
}
// ⭕️ 這樣即使沒有撰寫 address 也不會噴錯
const person1: Partial<IPerson> = {
name: "威爾豬",
age: 3,
};
// ❌
const person2: IPerson = {
name: "威爾豬",
age: 3,
};
我們再看另一個範例:
假設我們一樣有一個 IPerson 接口,它包含了名字、年齡和地址屬性,我們想要編寫一個函數,允許我們更新 IPerson 部分屬性的值:
interface IPerson {
name: string;
age: number;
address: string;
}
type TPartialPerson = Partial<IPerson>;
const updatePersonFn = (
person: IPerson,
updatePersonData: TPartialPerson
): IPerson => {
return { ...person, ...updatePersonData };
};
const person: IPerson = {
name: "威爾豬",
age: 3,
address: "皇后大道 123 街",
};
const updatePerson = updatePersonFn(person, { age: 4 });
console.log(updatePerson); // 輸出: { name: '威爾豬', age: 4, address: '皇后大道 123 街' }
在上面的範例中,我們首先聲明了 IPerson 接口。然後,我們創建了一個 updatePersonFn 函式,它接受一個接口為 IPerson 的 person 物件和一個類型別名為 TPartialPerson 的 updatePersonData 物件 (即 IPerson 的部分屬性),函式內使用展開運算符 (...) 將 updatePersonData 物件合併到原始 person 物件中,這樣就實現了部分屬性的更新。
除了創建新的類型別名外,我們也可以使用 接口繼承
來操作 Partial,看以下範例:
interface IPerson {
name: string;
age: number;
job: string;
}
// ⭕️ 使用接口繼承將所有屬性變成是可選的
interface IPartialPerson extends Partial<IPerson> {}
// ⭕️ 創建新的類型別名將所有屬性變成是可選的
type TPartialPerson = Partial<IPerson>;
const person1: IPartialPerson = { name: "威爾豬", job: "首富" };
const person2: TPartialPerson = { name: "威爾豬", age: 3 };
const person3: Partial<IPerson> = { name: "威爾豬" }; // ⭕️ 當然也可以直接這樣寫
上面範例的寫法都可以,就看我們個人或專案結構要怎麼使用比較方便了。
而和 Partial 相反的就是 Required<T>
,它是將物件型別中的 所有屬性變成必需屬性
。這個威爾豬比較少會用到,但還是介紹一下有這用法。
用法:Required<型別名稱>
看以下範例:
interface IPerson {
name: string;
age?: number;
address?: string;
}
type RequiredPerson = Required<IPerson>;
// ⭕️
const requiredPerson1: IPerson = {
name: "威爾豬",
address: "皇后大道 123 街",
};
// ❌ 全部屬性都需撰寫
const requiredPerson2: RequiredPerson = {
name: "威爾豬",
};
Pick<T, K>
可從一個型別 T 中選取特定屬性 K,也就是允許我們從一個物件型別中 選擇指定的屬性
,並創建一個新的類型別名。
用法:Pick<型別名稱, "選擇的屬性 1" | "選擇的屬性 2" | ......>
例如,我們還是一樣有一個 IPerson
的接口,我們想要創建一個新的類型別名,只包含 name 和 address 這兩個屬性,就可以使用 Pick
來實現:
interface IPerson {
name: string;
age: number;
address: string;
};
// 使用 Pick 創建選擇 name、address 屬性的 TPickPerson 類型別名
type TPickPerson = Pick<IPerson, "name" | "address">;
const pickPerson: TPickPerson = {
name: "威爾豬",
address: "皇后大道 123 街",
};
與 Pick 相反的就是 Omit<T, K>
可從一個型別 T 中排除特定屬性 K,也就是允許我們從一個物件型別中 排除指定的屬性
,並創建一個新的類型別名。
用法:Omit<型別名稱, "排除的屬性 1" | "排除的屬性 2" | ......>
如果我們想要依據 IPerson 接口來創建一個新的類型別名,並排除 age 屬性和 address 屬性,我們可以使用 Omit
:
interface IPerson {
name: string;
age: number;
address: string;
};
// 使用 Omit 創建排除 age、address 屬性的 TOmitPerson 類型別名
type TOmitPerson = Omit<IPerson, "age" | "address">;
const omitPerson: TOmitPerson = {
name: "威爾豬",
};
Utility 型別有非常多種,有興趣的小夥伴可以自行到官網查看 Utility 型別。以上只是小小的一部分,但威爾豬自己還滿常使用的,之後的章節威爾豬會再介紹 其它的 Utility 型別。
總結來說,使用 Partial、Required、Pick 和 Omit 等基本的 Utility 型別,可以 避免型別被重複定義
、創建更精確的類型別名
以及在不同的情境下可以 更靈活地運用型別
,就看我們運用哪一種方式來簡化程式碼,讓程式碼更具可讀性。